package com.zcily.utils;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class FileUtil {

    /**
     * 树形输出一个目录结构
     *
     * @param root         根目录
     * @param outputStream 输出流
     * @throws IOException
     */
    public static void printDirectoryTree(File root, OutputStream outputStream) throws IOException {
        doPrintDirectoryTree(new File[]{root}, "", outputStream);
    }

    /**
     * 树形输出多个目录结构
     *
     * @param files        要输出的多个目录
     * @param indent       行首的缩进；正常调用时，用""替代即可
     * @param outputStream 输出流
     * @throws IOException
     */
    public static void doPrintDirectoryTree(File[] files, String indent, OutputStream outputStream) throws IOException {
        int index = 0;
        for (File file : files) {
            if (index == files.length - 1) {
                outputStream.write((indent + "└── " + file.getName() + System.lineSeparator()).getBytes());
                if (file.isDirectory()) {
                    doPrintDirectoryTree(file.listFiles(), indent + "    ", outputStream);
                }
            } else {
                outputStream.write((indent + "├── " + file.getName() + System.lineSeparator()).getBytes());
                if (file.isDirectory()) {
                    doPrintDirectoryTree(file.listFiles(), indent + "│   ", outputStream);
                }
            }
            index++;
        }
    }

    /**
     * 重命名文件或目录
     *
     * @param file              需要操作的文件或目录
     * @param matchedString     匹配到的字符串
     * @param replacedString    替换后的字符串
     * @param sensitiveMatching 是否匹配大小写
     * @param includeDirectory  是否重命名目录
     * @param recursive         是否重命名子目录
     */
    public static void rename(File file, String matchedString, String replacedString, boolean sensitiveMatching, boolean includeDirectory, boolean recursive) {
        if (!file.exists()) {
            return;
        }
        Pattern pattern = null;
        if (sensitiveMatching) {
            pattern = Pattern.compile(matchedString);
        } else {
            pattern = Pattern.compile(matchedString, Pattern.CASE_INSENSITIVE);
        }
        doRename(new File[]{file}, pattern, replacedString, includeDirectory, recursive);
    }

    /**
     * 递归重命名文件和目录（严格区分大小写）
     *
     * @param file           当前文件或文件夹
     * @param matchedString  匹配到的字符串
     * @param replacedString 替换后的字符串
     */
    public static void rename(File file, String matchedString, String replacedString) {
        rename(file, matchedString, replacedString, true, true, true);
    }

    private static void doRename(File[] files, Pattern pattern, String replacedString, boolean includeDirectory, boolean recursive) {
        for (File file : files) {
            if (file.isFile()) {
                Matcher matcher = pattern.matcher(file.getName());
                String newName = matcher.replaceAll(replacedString);
                file.renameTo(new File(file.getParentFile(), newName));
            } else {
                if (includeDirectory) {
                    if (recursive) {
                        doRename(file.listFiles(), pattern, replacedString, includeDirectory, recursive);
                    }
                    Matcher matcher = pattern.matcher(file.getName());
                    String newName = matcher.replaceAll(replacedString);
                    file.renameTo(new File(file.getParentFile(), newName));
                }
            }
        }
    }

    /**
     * <p>替换文件中匹配到的字符串为指定字符串。</p>
     * <b>注意：如果是涉及多个文件的替换，那么被替换的文件应当使用同一字符集；否则可能会出现不可预计的错误。</b>
     *
     * @param file               需要操作的文件或目录
     * @param matchedString      匹配到的字符串
     * @param replacedString     替换后的字符串
     * @param charset            文件的字符集
     * @param sensitiveMatching  是否匹配大小写
     * @param recursive          是否递归子目录
     * @param extensionFileNames 替换指定扩展名的文件
     */
    public static void replaceText(File file, String matchedString, String replacedString, Charset charset, boolean sensitiveMatching, boolean recursive, String... extensionFileNames) {
        if (!file.exists()) {
            return;
        }
        Pattern pattern = null;
        if (sensitiveMatching) {
            pattern = Pattern.compile(matchedString);
        } else {
            pattern = Pattern.compile(matchedString, Pattern.CASE_INSENSITIVE);
        }
        List<String> extensionFileNameList = Arrays.stream(extensionFileNames).filter(StringUtils::isNotBlank).map(String::toLowerCase).collect(Collectors.toList());
        doReplaceText(new File[]{file}, pattern, replacedString, charset, recursive, extensionFileNameList);
    }

    /**
     * <p>替换文件中匹配到的字符串为指定字符串。</p>
     * <b>注意：如果是涉及多个文件的替换，那么被替换的文件应当使用同一字符集；否则可能会出现不可预计的错误。</b>
     *
     * @param file               需要操作的文件或目录
     * @param matchedString      匹配到的字符串
     * @param replacedString     替换后的字符串
     * @param charset            文件的字符集
     * @param extensionFileNames 替换指定扩展名的文件
     */
    public static void replaceText(File file, String matchedString, String replacedString, Charset charset, String... extensionFileNames) {
        replaceText(file, matchedString, replacedString, charset, true, true, extensionFileNames);
    }

    private static void doReplaceText(File[] files, Pattern pattern, String replacedString, Charset charset, boolean recursive, List<String> extensionFileNameList) {
        for (File file : files) {
            if (file.isFile()) {
                if (extensionFileNameList.size() > 0 && extensionFileNameList.stream().noneMatch(item -> file.getName().toLowerCase().endsWith("." + item))) {
                    continue;
                }

                String originalText = null;
                FileInputStream inputStream = null;
                try {
                    inputStream = new FileInputStream(file);
                    originalText = IOUtils.toString(inputStream, charset);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (inputStream != null) {
                        try {
                            inputStream.close();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }

                FileOutputStream outputStream = null;
                if (StringUtils.isNotBlank(originalText)) {
                    Matcher matcher = pattern.matcher(originalText);
                    String replacedText = matcher.replaceAll(replacedString);
                    try {
                        outputStream = new FileOutputStream(file);
                        IOUtils.write(replacedText, outputStream, charset);
                        outputStream.close();
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        if (outputStream != null) {
                            try {
                                outputStream.close();
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            } else {
                if (recursive) {
                    doReplaceText(file.listFiles(), pattern, replacedString, charset, recursive, extensionFileNameList);
                }
            }
        }
    }
}
